<?php

namespace Kangcg\Application\Http;

use Kangcg\Application\Base\Event;
use Kangcg\Application\Base\Trait\ErrorsTrait;

/**
 * curl请求
 * Class Curl
 * $curl = Curl::getInstall();
 * @package MyLibs\Help
 */
class HttpRequest
{
    use ErrorsTrait;

    public $charset = ''; //需要进行字符转换直接设置 - UTF-8

    const EVENT_REQUEST = 'request';

    /**
     * 单个请求
     * @param HttpRequestSetopts $setopts
     * @param $url
     * @param $method
     * @param $data
     * @param $isUseCommon
     * @return false
     * @throws \Exception
     */
    public function request(HttpRequestSetopts $setopts, $url, $method, $data = null, $isUseCommon = true)
    {
        $curl = $this->create($setopts, $url, $method, $data, $isUseCommon);
        $result = $this->execute($curl, false);
        if ($result === false) {
            return false;
        }

        $event = Event::create($this, self::EVENT_REQUEST, $result);
        Event::trigger($this, self::EVENT_REQUEST, $event);

        return $event->data;
    }

    /**
     * 批量執行
     * @param HttpRequestSetopts $setopts
     * @param array $urls [ ['url'=> 'url', 'data' => [], 'method' => 'GET']]
     * @param bool $isUseCommon
     * @return array
     * @throws \Exception
     */
    public function requestMulti(HttpRequestSetopts $setopts, array $urls, bool $isUseCommon = true)
    {
        $multi = curl_multi_init();
        $curls = [];
        foreach ($urls as $key => $item) {
            $url = $item['url'];
            $data = isset($item['data']) ? $item['data'] : '';
            $method = isset($item['method']) ? $item['method'] : HttpRequestSetopts::METHOD_GET;
            $curls[$key] = $this->create($setopts, $url, $method, $data, $isUseCommon);
            curl_multi_add_handle($multi, $curls[$key]);
        }

        // 执行批处理句柄
        do {
            curl_multi_exec($multi, $active);
        } while ($active);

        $data = [];
        $event = Event::create($this, self::EVENT_REQUEST);
        foreach ($curls as $k => $curl) {
            $result = $this->execute($curl);
            if (false !== $result) {
                $event->data = $result;
                $event->handled = false;
                Event::trigger($this, self::EVENT_REQUEST, $event);
                $result = $event->data;
            }

            curl_multi_remove_handle($multi, $curl);//释放资源
            $data[$k] = $result;
        }

        curl_multi_close($multi);
        return $data;
    }

    /**
     * 获取执行结果
     * @param $isMulti bool 是否是单例请求
     * @return bool|mixed
     */
    protected function execute($curl, bool $isMulti = true)
    {
        $result = $isMulti ? curl_multi_getcontent($curl) : curl_exec($curl);
        if ($result === false) {
            $this->setErrorMessage(curl_error($curl));
        }

        return $result;
    }

    /**
     * 对获取的结果 进行编码转换
     * @param string $curlInfo curl响应 content_type
     * @param string $result 要转换的字符串
     * @param string $out_charset 输出编码 -- UTF-8
     * @return string
     */
    public function mb_convert_encoding($result, $contentType, $out_charset)
    {
        $out_charset = strtoupper($out_charset);
        preg_match('/charset=(.*)/i', $contentType, $matches);
        $charset = strtoupper(isset($matches[1]) ? $matches[1] : 'UTF-8');
        if (in_array($charset, [
            'EXT/HTML',
            $out_charset
        ])) {
            return $result;
        }

        $string = mb_convert_encoding($result, $out_charset, $charset);
        return $string ? $string : $result;
    }


    /**
     * 获取创建对象
     * @return \CurlHandle
     */
    private function create(HttpRequestSetopts $setopts, $url, $method, $data, $isUseCommon)
    {
        $curl = curl_init();
        $setopts->setUrlSetopts($url, $method, $data);
        !$isUseCommon or $setopts->useUseCommon();
        curl_setopt_array($curl, $setopts->getSetopts());

        return $curl;
    }


    /**
     * 获取请求实例对象
     * @return static
     */
    public static function getInstall(): static
    {
        if (self::$_install === null) {
            self::$_install = new self();
        }

        return self::$_install;
    }

    private function __clone(): void
    {

    }

    protected static $_install = null;
}
